﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net;
using System.Net.Mime;
using System.Text;
using System.Threading.Tasks;
using System.Timers;
using System.Web;
using FANN;
using FANN.Net;
using System.Windows.Forms;

namespace Node
{
    public class Thread
    {

        private FANN.Net.TrainingData _dataset;
        private FANN.Net.NeuralNet _net;

        private List<NodeWork> works;
       //  private string url = @"http://ctfof.azurewebsites.net/";
         private string url = @"http://localhost:16838/";

        //   private string url = @"http://localhost/WebApplication/";
        private System.Timers.Timer timer;
        private string id;


        public void Run()
        {try
            {

            timer = new System.Timers.Timer();
            timer.Interval = 30000;
            timer.Elapsed += SendState;

            System.Threading.Thread t = new System.Threading.Thread(timer.Start);
            t.Start();

            while (true)
            {
                Connect();
                System.Threading.Thread.Sleep(30000);
            }
        }
              catch (Exception ex)
            {
                lock (o)
                {
                    if (ex.Message == new OutOfMemoryException().Message)
                    Application.Restart(); 
                }
            }
        }
        public void Connect()
        {

            try
            {

                works = new List<NodeWork>();

                using (var wb = new WebClient())
                {

                    var response = wb.DownloadData(url + "/api/node");
                    string pageHtml = Encoding.ASCII.GetString(response);
                    id = pageHtml.Trim('"');
                    Console.WriteLine("Connect to server id=" + id);

                }

                while (GetWork())
                {
                    System.Threading.Thread.Sleep(60000);
                }
                Console.WriteLine("Lost connect id=" + id);
            }
            catch (Exception ex)
            {
                lock (o)
                {
                    if (ex.Message == new OutOfMemoryException().Message)
                    Application.Restart(); 
                }
            }

        }

        private static object o = new object();

        public bool GetWork()
        {
            using (var wb = new WebClient())
            {

                string postData = "id=" + id + "&state=0";
                wb.Headers.Add("Content-Type", "multipart/form-data");
                byte[] byteArray = Encoding.ASCII.GetBytes(postData);

                var response = wb.UploadData(url + "api/node", byteArray);
                string pageHtml = Encoding.ASCII.GetString(response);
                var text = pageHtml.Trim('"');
                if (text == "true")
                {
                    Console.WriteLine("no work id=" + id);
                    return true;
                }
                if (text == "false")
                {
                    Console.WriteLine("no reg id=" + id);
                    return false;
                }
                var w = new NodeWork(text);
                Threin(w);
                return true;
            }
        }

       


        public void Threin(NodeWork work)
        {

            if (works.Count == 0 || works[works.Count - 1].IdWork != work.IdWork ||
                (works[works.Count - 1].EndSet + 1) != work.StartStep)
            {
                using (var wb = new WebClient())
                {
                    var file = Path.GetTempFileName();
                    var dir = Path.GetRandomFileName();
                    lock (o)
                    {
                        wb.DownloadFile(url + "DataSet/GetFile/" + work.IdDataSet, file);
                        ZipFile.ExtractToDirectory(file, dir);
                        _dataset = new FANN.Net.TrainingData();
                        _dataset.ReadTrainFromFile(Directory.GetFiles(dir,"*.dat")[0]);
                        File.Delete(file);
                        Directory.Delete(dir, true);
                    }
                    if (work.StartStep != 0)
                    {
                        lock (o)
                        {
                            file = Path.GetTempFileName();
                            dir = Path.GetRandomFileName();
                            wb.DownloadFile(url + "Temp/GetFile/" + work.IdStartTemp, file);
                            ZipFile.ExtractToDirectory(file, dir);
                            _net = new FANN.Net.NeuralNet();
                            _net.CreateFromFile(Directory.GetFiles(dir, "*.ann")[0]);
                            File.Delete(file);
                            Directory.Delete(dir, true);
                        }
                    }
                    else
                    {
                        var set = work.Param.Split(';');
                        _net = new NeuralNet();
                        var ls = set[3].Split('.');
                        var laers = new uint[ls.Length];
                        for (var i = 0; i < ls.Length; i++)
                            laers[i] = Convert.ToUInt16(ls[i]);
                        _net.CreateStandardArray(laers);
                        _net.SetActivationFunctionHidden(
                            (ActivationFunction)Enum.Parse(typeof(ActivationFunction), set[1], true));
                        _net.SetActivationFunctionOutput(
                            (ActivationFunction)Enum.Parse(typeof(ActivationFunction), set[2], true));
                        _net.SetTrainingAlgorithm(
                            (TrainingAlgorithm)Enum.Parse(typeof(TrainingAlgorithm), set[0], true));
                        _net.SetActivationSteepnessHidden(0.5);
                        _net.SetActivationSteepnessOutput(0.5);
                        _net.SetBitFailLimit(0.035);
                        _net.SetLearningMomentum(0);
                        _net.SetLearningRate(0.7f);
                        _net.SetTrainErrorFunction(ErrorFunction.Linear);
                        _net.SetTrainStopFunction(StopFunction.MSE);

                    }

                }


            }
            Console.WriteLine("start id=" + id + " workid=" + work.IdWork + " step=" + work.StartStep);
            works.Add(work);
            double val = 0;
            for (int i = work.StartStep; i <= work.EndSet; i++)
                val = _net.TrainEpoch(_dataset);
            lock (o)
            {
                if (val == 0)
                    Application.Restart();

                var dir1 = Path.GetRandomFileName();
                Directory.CreateDirectory(dir1);
                _net.Save(dir1 + "\\ann.ann");
                ZipFile.CreateFromDirectory(dir1, dir1 + ".zip");
                Directory.Delete(dir1, true);
                var f = File.ReadAllBytes(dir1 + ".zip");
                File.Delete(dir1 + ".zip");
                using (var wb = new WebClient())
                {

                    string postData = "id=" + id + "&state=2&workId=" + work.Id + "&value=" + val.ToString(CultureInfo.InvariantCulture) + "&fileLenght=" +
                                      f.Length + "&file=";
                    wb.Headers.Add("Content-Type", "multipart/form-data");
                    byte[] byteArray = Encoding.ASCII.GetBytes(postData).Concat(f).ToArray();
                    wb.UploadData(url + "api/node", byteArray);



                }
                Console.WriteLine("start id=" + id + " workid=" + work.IdWork + " end step=" + work.StartStep + " valuse=" + val.ToString(CultureInfo.InvariantCulture));
            }

            GetWork();
        }


        public void SendState(object source, ElapsedEventArgs e)
        {
            if (id!="")
            using(var wb = new WebClient())
            {

                try
                {
                    string postData = "id=" + id + "&state=1";
                    wb.Headers.Add("Content-Type", "multipart/form-data");
                    ;
                    wb.UploadData(url + "api/node", Encoding.ASCII.GetBytes(postData));
                    Console.WriteLine("send " + id);
                }
                catch (Exception ex)
                {
                    lock (o)
                    {
                        if (ex.Message == new OutOfMemoryException().Message)
                            Application.Restart();
                    }
                }
            }
        }
    }

    public class NodeWork
    {

        public string Param;
        public string Id;
        public int IdWork;
        public int IdDataSet;
        public int StartStep;
        public int EndSet;
        public DateTime StartTime;
        public DateTime EndTime;
        public int IdStartTemp;
        public int IdSEndTemp;


        public NodeWork(string text)
        {
            var p = text.Split('|');
            Id = p[0];
            Param = p[1];
            IdWork = Convert.ToInt32(p[2]);
            IdDataSet = Convert.ToInt32(p[3]);
            StartStep = Convert.ToInt32(p[4]);
            EndSet = Convert.ToInt32(p[5]);
            IdStartTemp = Convert.ToInt32(p[6]);


        }



    }
}
